# -*- coding: utf-8 -*-
"""
Created on Mon Sep 21 16:23:07 2020

@author: Robin
"""
import pandas as pd
#import numpy as np

from lib.performance import (factor_information_coefficient,get_indicator_analysis,
                             back_testing,minTE2,back_testing_with_weights,
                             weighted_in_oneday_by_value)
from lib.utils import *
#from lib.dataExtract import (cal_inddf,cal_stockdf,cal_groupby,cal_weightdf)
from lib.dataExtract2 import (cal_inddf,cal_stockdf,cal_groupby,cal_weightdf,
                              cal_indexdf,cal_market_value_df)
from lib.multimodel import *
#from lib.variable_template import *


"""
传入参数报错
"""
class NonDataError(Exception):
    pass

"""
因子分析的参数
"""
class AnalysisConfig():
    def __init__(self,extreme_method='mad',scale_method = 'normal',
                 ic_method = 'rank',ret_method = 'cap',turnover_method = 'count',
                 group_num = 5,industry_neu = False,
                 industry_type = 'sw_1'):
        
        self.extreme_method = extreme_method       # 去极值的方法
        self.scale_method = scale_method           # 标准化的方法
        self.ic_method = ic_method                 # ic计算方法
        self.ret_method = ret_method               # 组合收益率加权方法
        self.turnover_method = turnover_method     # 换手率计算方法
        self.group_num = group_num                 # 当为大于等于2的整数时，对股票平均分组；当为（0,0.5）之间的浮点数，对股票分为3组，前group_num%为G01，后group_num%为G02，中间为G03
        #self.ind_direction = ind_direction        # 因子排序方向
        self.industry_neu = industry_neu           # 分组时是否采用行业中性
        self.industry_type = industry_type         # 行业分类方法
        #self.industry_level = industry_level       # 行业分类等级
    
    def printpara(self):
        """
        数据查看
        """   
        print('extreme_method: ' + self.extreme_method)      
        print('scale_method: ' + self.scale_method)
        print('ic_method: ' + self.ic_method)
        print('ret_method: ' + self.ret_method)
        print('turnover_method: ' + self.turnover_method)
        print('group_num: ' + self.group_num)
        #print('ind_direction: ' + self.ind_direction)
        print('industry_neu: ' + self.industry_neu)
        print('industry_type: ' + self.industry_type)
        print('industry_level: ' + self.industry_level)

"""
传入AlphaModel的参数，用于提取相关资产价格以及因子数据
"""
class AlphaModelpara():
    def __init__(self,stock_codes,ind_codes,begdate,enddate,
                 period=1,bench_code='000905.SH',
                 is_index = True,include_st = False,
                 include_suspend = False,include_new_stock = False,
                 ipo_days = 60):
        self.stock_codes = stock_codes               # 股票代码：字符串，或者list
        self.ind_codes = ind_codes                   # 因子代码：字符串，或者list
        self.begdate = begdate                       # 开始时间 
        self.enddate = enddate                       # 结束时间
        self.period = period                         # 数据周期
        self.is_index = is_index                     # 是否为股指
        self.bench_code = bench_code                 # 基准代码(用来对比收益)（待修改，当is_index为True时，默认为stock_code, 其他默认为'000905.SH'）
        
        # 以下参数暂时不实现相关功能
        self.include_st = include_st                 # 是否包含ST股票
        self.include_suspend = include_suspend       # 是否包含当天停牌股票
        self.include_new_stock = include_new_stock   # 是否包含当天新上市股票
        self.ipo_days = ipo_days                     # 新股上市ipo_days天以内的去除

    def printdata(self):
        print('stock_codes: ' + self.stock_codes)            
        print('ind_codes: ' + self.ind_codes)
        print('begdate: ' + self.begdate)   
        print('enddate: ' + self.enddate)    
        print('period: ' + self.period)
        print('is_index: ' + self.is_index) 
        print('bench_code: ' + self.bench_code)
        print('include_st: ' + self.include_st) 
        print('include_suspend: ' + self.include_suspend)
        print('include_new_stock: ' + self.include_new_stock)
        print('ipo_days: ' + self.ipo_days)

"""
Alpha模型类
"""
class AlphaModel():
    def __init__(self,stock_codes,ind_codes,begdate,enddate,
                 period=1,bench_code='000905.SH',is_index = True,
                 include_st = False,include_suspend = False,
                 include_new_stock = False,ipo_days = 60,
                 model_type = 'score',combine_type = 'ic_ir'):
        # 传入参数保存
        self.para = AlphaModelpara(stock_codes,ind_codes,begdate,enddate,
                 period,bench_code,is_index,include_st,include_suspend,
                 include_new_stock,ipo_days)
        # 根据传入参数提取股票数据及因子数据
        self.stock = cal_stockdf(stock_codes,begdate,enddate,period,
                                include_st,include_suspend,include_new_stock,
                                ipo_days) #导入股票池中所有股票的收盘价数据
        self.factors = cal_inddf(self.stock.index, ind_codes, begdate, enddate)     # 因子数据的提取
        self.groupby = None
        self.index_data=cal_indexdf(bench_code,begdate,enddate)
        #index_data是指数的收盘价数据，bench_code默认为000905.SH（中证500）
        self.market_value=cal_market_value_df(stock_codes,begdate,enddate)
        #market_value是股票的市值数据
        
        #industry_weight还需要导入
        
        if ~is_index & (bench_code is None):
            self.stock_weight = None
        elif is_index:
            self.stock_weight = cal_weightdf(self.stock.index,
                                             base_code=bench_code)
        elif (bench_code is not None):
            self.stock_weight = cal_weightdf(self.stock.index,
                                             base_code=bench_code)
        '''
        初始化成员变量
        '''
        # 因子分析
        self.ind_code = None    # 目前正在分析的单因子
        self.factor = None    # 目前正在分析的单因子数据
        self.factor_data = None    # 对齐数据
        self.analysis_config = AnalysisConfig(extreme_method='mad',scale_method = 'normal',
                 ic_method = 'rank',ret_method = 'cap',turnover_method = 'count',
                 group_num = 5,industry_neu = False,
                 industry_type = 'sw_1')   # 参数配置
        self.indicator_analysis = {}    # 分析结果
        # 因子组合
        self.select_ind = self.para.ind_codes    # 用于组合的因子，初始化为全部因子   
        self.model_type = model_type   # 选股的模型，包括打分法'score'和回归法'regress'
        self.combine_type = combine_type #因子组合的模型，包括ic_ir加权法'ic_ir'和主成分分析法'pca'
        # 选股结果
        self.stock_choosed = None     
        self.stock_choosed_type = 0   # 1：单日期，2：单日期+行业分类，3：多日期，4：多日期+行业分类
        self.stock_choosed_with_weight = None
        self.stock_choosed_date=None

    def run_single_indicator_analysis(self,ind_code,print_factor_combine_info = True):
        """
        进行单因子分析，将分析结果存入一个结构体
        """        
        # 0.数据处理
        # 数据提取
        if ind_code is not None:
            factor = self.factors[ind_code]    # 提取一个因子的数据
            self.factor = factor.copy(deep = True)
        elif (ind_code is None) & (self.factor is None):
            raise NonDataError("No factor Data")    # 报错
        else:
            factor = self.factor.copy(deep = True)    # 使用提前定义好的单因子
            
        factor_data = cal_factor_data(self.stock, factor, 
                                      groupby=self.groupby,
                                      print_factor_combine_info = True)    # 生成对齐好的数据
        
        '''
        对去极值的方法进行操作
        '''
        # 1.获取统计分析结果        
        indicator_analysis = get_indicator_analysis(factor_data,                           
                                                    scale_method = self.analysis_config.scale_method,
                                                    ic_method = self.analysis_config.ic_method,                           
                                                    ret_method = self.analysis_config.ret_method,
                                                    turnover_method = self.analysis_config.turnover_method,
                                                    group_num = self.analysis_config.group_num,                           
                                                    #ind_direction = self.analysis_config.ind_direction,
                                                    industry_neu = self.analysis_config.industry_neu,
                                                    industry_type = self.analysis_config.industry_type)
        # 数据保存
        self.ind_code = ind_code
        self.factor_data = factor_data.copy(deep = True)
        self.indicator_analysis = indicator_analysis
        
        # 将计算结果返回
        return indicator_analysis

    def run_multi_indicators_analysis(self,select_ind = None, 
                                      print_factor_combine_info = False):
        """
        进行多因子分析，将分析结果存入一个结构体
        多因子分析的方式会影响到选股模型
        """
        # 数据提取
        if (select_ind == None):
            select_ind = self.select_ind
        self.select_ind = select_ind
        combine_type = self.combine_type
        stock = self.stock.copy(deep = True)
        select_factors = self.factors[select_ind]
        groupby=self.groupby
        # 因子合成
        combined_factor_df = get_combined_factor_df(select_factors,stock,combine_type)
        combined_factor_df.name = 'combine_factor'
        # 1.获取统计分析结果
        # 生成对齐数据
        factor_data = cal_factor_data(stock, combined_factor_df,
                                      groupby = groupby,
                                      print_factor_combine_info = False)        
        indicator_analysis = get_indicator_analysis(factor_data,                           
                                                    scale_method = self.analysis_config.scale_method,
                                                    ic_method = self.analysis_config.ic_method,                           
                                                    ret_method = self.analysis_config.ret_method,
                                                    turnover_method = self.analysis_config.turnover_method,
                                                    group_num = self.analysis_config.group_num,                           
                                                    #ind_direction = self.analysis_config.ind_direction,
                                                    industry_neu = self.analysis_config.industry_neu,
                                                    industry_type = self.analysis_config.industry_type)
        # 存储数据
        self.factor = combined_factor_df.copy(deep = True)
        self.indicator_analysis = indicator_analysis
        self.factor_data = factor_data.copy(deep = True)   
        
        return indicator_analysis

    def select_stocks_by_model(self,datestr,stock_num,by_group = False,
                               select_ind = None,lookbackPeriod = 50):
        """
        根据因子大小对股票池进行筛选
        """         
        # 1. 数据准备
        model_type = self.model_type 
        if by_group:
            groupby = self.groupby
        else:
            groupby = None
        
        
        # 2. 选股
        if isinstance(datestr,str) & (model_type == 'score'): 
#            IC的均值，确定该因子是正向还是反向
            ic_mean=self.indicator_analysis.ic_analysis.ic_stats['IC_mean'][0]
            factor = self.factor.copy(deep = True)
            stock_choosed,stock_choosed_type = get_stock_list_by_score(factor,groupby,datestr,stock_num,ic_mean)
            self.stock_choosed_date=datestr #如果是按单个日期选股，把这个日期保存下来，后续使用
            
        else:
            if select_ind == None:
                select_ind = self.select_ind
            else:
                self.select_ind = select_ind
            stock = self.stock
            factors = self.factors[select_ind]
            if isinstance(datestr,str) & (model_type == 'regress'):
                stock_choosed,stock_choosed_type = get_stock_list_by_regression(factors,stock,groupby,datestr,stock_num)                
            elif isinstance(datestr,list) & (model_type == 'score'):
                combine_type = self.combine_type
                stock_choosed,stock_choosed_type = \
                    get_stock_list_by_score_by_dates(factors,stock,groupby,datestr,stock_num,lookbackPeriod,combine_type)
            elif isinstance(datestr,list) & (model_type == 'regress'):
                stock_choosed,stock_choosed_type = \
                    get_stock_list_by_regress_by_dates(factors,stock,groupby,datestr,stock_num,lookbackPeriod)

        # 3. 数据保存
        self.stock_choosed_type = stock_choosed_type
        self.stock_choosed = stock_choosed
        return stock_choosed

    def add_weight_to_stock_choose(self):
        '''
        以个股在沪深300中的权重（中证公司给出的数据）给选出的股票加权重
        '''
        if self.stock_weight is None:
            raise NonDataError("No Weight Data")
        
        '''
        self.stock_weight是一个双索引的DataFrame,索引是日期和股票名，
        值是该股票当天在沪深300中的配置权重，单位是%
        
        date        asset    
        2007-01-31  000006.SZ    0.470
                    000016.SZ    0.240
                    000028.SZ    0.140
        ...
        
        2020-08-31  603328.SH    0.070
                    603338.SH    0.367
                    603355.SH    0.044
                    603377.SH    0.122
        '''
        stock_choosed = self.stock_choosed
        stock_weight = pd.merge(self.groupby,self.stock_weight,
                                left_index = True,right_index = True)\
                                .reset_index(level = 'asset')\
                                .rename(columns = {'i_weight':'weight'})

        stock_weight.set_index(pd.to_datetime(stock_weight.index),inplace = True)
        
        '''
        stock_weight是一个DataFrame,索引是日期
                        asset     industry  weight
        date                                  
        2007-01-31  000006.SZ      房地产   0.470
        2007-01-31  000016.SZ     可选消费   0.240
        2007-01-31  000028.SZ     医疗保健   0.140
        2007-01-31  000032.SZ     信息技术   0.090
        2007-01-31  000033.SZ     可选消费   0.080
        '''
        if self.stock_choosed_type == 4 :      
            # 生成 MultiIndex
            stock_tup = tuple()
            for datestr in stock_choosed.keys():
                stock_set = set()
                for industry in stock_choosed[datestr].keys():
                    stock_set = (stock_set | set(stock_choosed[datestr][industry]))
                    stock_list = list(stock_set)
                stock_tup = stock_tup + tuple((pd.to_datetime(datestr), b) for b in stock_list )
            stock_choosed_index = pd.MultiIndex.from_tuples(stock_tup,names=['date','asset'])
            stock_choosed_index = stock_choosed_index.to_frame(index=False).set_index('date')
            stock_weight['dateraw'] = stock_weight.index
            # 找到weight所对应的索引
            stock_choosed_df = pd.merge_asof(stock_choosed_index,
                               stock_weight,
                               left_index = True,right_index = True,
                               left_by = 'asset',right_by = 'asset').reset_index()
            
            '''
            stock_choosed_df是这样的dataframe
                       date      asset    industry  weight    dateraw
            0    2020-08-28  300324.SZ     信息技术   0.173  2020-07-31
            1    2020-08-28  000519.SZ       工业   0.142    2020-07-31
            2    2020-08-28  000750.SZ       金融   0.389    2020-07-31
            3    2020-08-28  600823.SH      房地产   0.089   2020-07-31
            4    2020-08-28  601228.SH       工业   0.114    2020-07-31
            
            date是选股日期，dateraw是对应的权重日期（因为权重只有每个月月底数据）
            '''
            
            # 计算行业权重
            stock_weight1 = stock_weight.loc[pd.to_datetime(pd.unique(stock_choosed_df['dateraw']))]    # 选择相应日期数据
            stock_weight1 = stock_weight1.groupby(['dateraw','industry'])['weight'].sum()\
                                         .reset_index(level=['dateraw','industry'])\
                                         .rename(columns = {'weight':'indu_weight'})   # 每个行业的权重
            
            '''
            stock_weight1是一个dataframe，记录了在我们选中的股票中各个行业占的总权重
                  dateraw     industry  indu_weight
            0  2020-07-31     信息技术       19.113
            1  2020-07-31     公用事业        2.237
            2  2020-07-31     医疗保健       14.412
            3  2020-07-31     可选消费       11.579
            4  2020-07-31       工业         17.303
            '''                            
            stock_choosed_df = pd.merge(stock_choosed_df,stock_weight1[['dateraw','indu_weight','industry']],
                                        on = ['industry','dateraw'])
            
            '''
            现在stock_choosed_df是这个样：
                       date      asset     industry  weight  dateraw  indu_weight
            0    2020-08-28  300324.SZ     信息技术   0.173 2020-07-31       19.113
            1    2020-08-28  300010.SZ     信息技术   0.285 2020-07-31       19.113
            2    2020-08-28  600171.SH     信息技术   0.185 2020-07-31       19.113
            3    2020-08-28  002815.SZ     信息技术   0.126 2020-07-31       19.113
            4    2020-08-28  002368.SZ     信息技术   0.205 2020-07-31       19.113
            '''
            
            stock_weight2 = stock_choosed_df.groupby(['date','industry'])['weight'].sum()\
                                            .reset_index(level=['date','industry'])\
                                            .rename(columns = {'weight':'buy_weight'})   # 每个行业中选中的股票的权重之和
            '''
            stock_weight2   储存每一天该行业的总权重
                     date     industry  buy_weight
            0  2020-08-28     信息技术       5.653
            1  2020-08-28     公用事业       2.237
            2  2020-08-28     医疗保健       7.502
            3  2020-08-28     可选消费       5.618
            4  2020-08-28       工业         5.710
            '''
            stock_choosed_df = pd.merge(stock_choosed_df,stock_weight2[['date','buy_weight','industry']],
                                        on = ['industry','date'])
            # 个股在组合中投资占比：单位为百分数
            stock_choosed_df['weight'] = stock_choosed_df['weight'] / stock_choosed_df['buy_weight'] * stock_choosed_df['indu_weight']
            stock_choosed_df = stock_choosed_df.set_index(['date','asset'])[['weight']]
            
            '''
            stock_choosed_df是一个双索引（日期，股票名）的dataframe，内容是权重
            '''
            # 记录选股结果
            stock_choosed_dict = dict([(kk,stock_choosed_df.loc[kk].to_dict()['weight']) \
                                     for kk in stock_choosed_df.index.levels[0]]) 

            '''
            stock_choosed_dict是一个字典，键是选股的日期，值也是一个字典
            值的字典的键是股票名，值是其权重
            '''           
            self.stock_choosed_with_weight = stock_choosed_dict
            return stock_choosed_df
            
    def set_model_para(self,stock_codes = None,ind_codes = None,begdate = None,
                    enddate = None,period = None,bench_code = None,is_index = None,
                    include_st = None,include_suspend = None,include_new_stock = None,
                    ipo_days = None):
        """
        改变类的参数
        参数改变之后，会重新提取数据
        """
        if (stock_codes == None):
            stock_codes = self.para.stock_codes
        if (ind_codes == None):
            ind_codes = self.para.ind_codes
        if (begdate == None):
            begdate = self.para.begdate
        if (enddate == None):
            enddate = self.para.enddate
        if (period == None):
            period = self.para.period
        if (bench_code == None):
            bench_code = self.para.bench_code
        if (is_index == None):
            is_index = self.para.is_index
        if (include_st == None):
            include_st = self.para.include_st
        if (include_suspend == None):
            include_suspend = self.para.include_suspend
        if (include_new_stock == None):
            include_new_stock = self.para.include_new_stock
        if (ipo_days == None):
            ipo_days = self.para.ipo_days
        # 1. 改变参数
        self.para = AlphaModelpara(stock_codes,ind_codes,begdate,enddate,
                 period,bench_code,is_index,include_st,include_suspend,
                 include_new_stock,ipo_days)
        # 2. 更新因子与股票数据
        # 根据传入参数提取股票数据及因子数据
        self.stock = cal_stockdf(stock_codes,begdate,enddate,period,
                                include_st,include_suspend,include_new_stock,
                                ipo_days)
        self.factors = cal_inddf(self.stock.index, ind_codes, begdate, enddate)     # 因子数据的提取
        self.groupby = None
        
        if ~is_index & (bench_code is None):
            self.stock_weight = None
        elif is_index:
            self.stock_weight = cal_weightdf(self.stock.index,
                                             base_code=bench_code)
        elif (bench_code is not None):
            self.stock_weight = cal_weightdf(self.stock.index,
                                             base_code=bench_code)
               
        # 3. 重新初始化成员变量，用于存储分析结果
        self.ind_code = None    # 目前正在分析的单因子
        self.factor = None
        self.factor_data = None   
        self.indicator_analysis = {}
        self.select_ind = self.para.ind_codes    # 用于组合的因子，初始化为全部因子   
        self.model_type = 'score'   # 因子组合的模型

    def set_analysis_config(self,extreme_method=None,scale_method = None,ic_method = None,
                            ret_method = None, turnover_method = None,group_num = None,
                            industry_neu = None,industry_type = None,
                            industry_level = None):
        """
        改变因子分析的参数
        改变之后，会更新行业数据，但是不会改变之前的分析结果
        """
        flag_change_indi = 0
        if extreme_method == None:
            extreme_method = self.analysis_config.extreme_method
        if scale_method == None:
            scale_method = self.analysis_config.scale_method
        if ic_method == None:
            ic_method = self.analysis_config.ic_method
        if ret_method == None:
            ret_method = self.analysis_config.ret_method
        if turnover_method == None:
            turnover_method = self.analysis_config.turnover_method
        if group_num == None:
            group_num = self.analysis_config.group_num
#        if ind_direction == None:
#            ind_direction = self.analysis_config.ind_direction
        if industry_neu == None:
            industry_neu = self.analysis_config.industry_neu
        if industry_type == None:
            industry_type = self.analysis_config.industry_type
        else:
            flag_change_indi = 1
        if industry_level == None:
            industry_level = self.analysis_config.industry_level
        else:
            flag_change_indi = 1
        # 参数配置更新
        self.analysis_config = AnalysisConfig(extreme_method,scale_method,
                 ic_method,ret_method,turnover_method,
                 group_num,industry_neu,industry_type,industry_level)
        # 行业数据更新
        if flag_change_indi==1:
            self.set_groupby(industry_type,industry_level)   
    
    def set_groupby(self,industry_type='sw_1',industry_level=1,to_none = False):
        '''
        行业数据更新
        '''
        if to_none:
            # 不分组
            self.groupby = None
        else:
            self.groupby = cal_groupby(self.stock.index, industry_type='sw_1')
        '''
        groupby : pd.Series - MultiIndex or dict
                    Either A MultiIndex Series indexed by date and asset,
                    containing the period wise group codes for each asset, or
                    a dict of asset to group mappings. If a dict is passed,
                    it is assumed that group mappings are unchanged for the
                    entire time period of the passed factor data.
        '''         

    def set_model_type(self,model_type):
        """
        改变因子选股的方式
        """
        self.model_type = model_type 

    def set_combine_type(self,combine_type):
        """
        改变因子组合的方式
        """
        self.combine_type = combine_type       
        
    def get_model_para(self):
        """
        查看初始化模型的参数
        """
        self.para.printdata()

    def get_analysis_config(self):
        """
        查看因子分析的参数配置
        """
        self.analysis_config.printdata()

    def back_test_choosen_stock(self,period='daily',with_weights=False):
        '''
        with_weights=False代表不输入权重，即默认等权重；
        with_weights=True代表已有权重，即self.stock_choosed_with_weight是有内容的，
        按照给定的权重赋权。
        '''
        if with_weights==False:
            return back_testing(stock_data=self.stock,stock_choosed_dict=self.stock_choosed,
                            end_date=self.para.enddate,period=period,
                            stock_choosed_type=self.stock_choosed_type)
        elif with_weights==True:
            if self.stock_choosed_with_weight==None:
                print('需要先运行运行给选股结果赋权重的函数')
            else:
                return back_testing_with_weights(stock_data=self.stock,
                                                  stock_choosed_dict=self.stock_choosed_with_weight,
                                                  end_date=self.para.enddate,
                                                  period=period)
        else:
            pass
    
    
    #使用最小化跟踪误差的方法给选出的股票赋权
    def add_weight_to_stock_choose_minTE(self):
        '''
        index_data是一个DataFrame，内容为指数收盘价
        
                    000905.SH
        Date                 
        2015-11-10     0.4137
        2015-11-11     1.4476
        2015-11-12    -0.0288
       
        注：index_data和self.stock的频率须一致，都是日频或都是周频
        '''
            
        #市值数据
        market_value=self.market_value

        #将选股结果处理成需要的格式
        stock_choosed=self.stock_choosed 
        stock_tup=tuple()
        if self.stock_choosed_type==3: #不行业分类，按日期序列
            datelist=list(stock_choosed.keys())
            for datestr in datelist:
                stock_tup=stock_tup+tuple((pd.to_datetime(datestr),b) for b in stock_choosed[datestr])
            
        elif self.stock_choosed_type==4: #行业分类，按日期序列
            datelist=list(stock_choosed.keys())
            for datestr in datelist:
                for industry in stock_choosed[datestr].keys():
                    stock_list = list(stock_choosed[datestr][industry])
                    stock_tup = stock_tup + tuple((pd.to_datetime(datestr), b) for b in stock_list )
                        
        elif self.stock_choosed_type==1: #不行业分类，按日期
            stock_tup=stock_tup+tuple((pd.to_datetime(self.stock_choosed_date),b) for b in stock_choosed)
            datelist=[self.stock_choosed_date]
            
        elif self.stock_choosed_type==2:
            datelist=[self.stock_choosed_date]
            for industry in stock_choosed.keys():
                stock_list=list(stock_choosed[industry])
                stock_tup=stock_tup+tuple((pd.to_datetime(datestr),b) for b in stock_list)
            
        else:
            pass
        
        stock_choosed_index = pd.MultiIndex.from_tuples(stock_tup,names=['date','asset'])
        stock_choosed_index = stock_choosed_index.to_frame(index=False).set_index('date')
        
#        begdate=pd.to_datetime(self.para.begdate)
#        enddate=pd.to_datetime(self.para.enddate)
        
        stock_data=self.stock.copy()
        stock_ret=stock_data.apply(lambda data: data.pct_change())  
        stock_ret=stock_ret.dropna().loc[datelist]
        
        #将输入指数的价格计算出收益率
        index_data=self.index_data.copy()
        index_ret=index_data.apply(lambda data: data.pct_change())
        #第一天无法计算收益率，故删掉这一天的数据
        index_ret=index_ret.dropna().loc[datelist]  

        
        #现在开始按日期给当天选出的股票赋权
        datelist=pd.to_datetime(datelist)
        index_ret_use=index_ret.loc[datelist]
        
        df_list=[]
        for date in datelist:
            stock_list_today=list(stock_choosed_index.loc[date]['asset'])
            
            #计算出我们所选的股票的收益率
            stock_ret_use=stock_ret[stock_list_today]

            weights=minTE2(index_ret_use,stock_ret_use,market_value)
            
            df_stock=stock_choosed_index.loc[date]
            df_stock['weight']=weights
            df_list.append(df_stock)
        
        stock_choosed_df=pd.concat(df_list)
        stock_choosed_df=stock_choosed_df.reset_index()
        stock_choosed_df=stock_choosed_df.set_index(['date','asset'])
            
        # 记录选股结果
        stock_choosed_dict = dict([(kk,stock_choosed_df.loc[kk].to_dict()['weight']) \
                                 for kk in stock_choosed_df.index.levels[0]]) 
    
        self.stock_choosed_with_weight=stock_choosed_dict
        return stock_choosed_df
        
    #对按照行业分类的选股结果-进行按照市值加权
    def add_weight_to_stock_choose_value_industry(self,industry_weight):
        
        '''
        stock_choosed为dict格式：
        key为日期，value是按照行业分类的选股结果（选股结果也为dict格式）
        
        market_value为dataframe格式：
        index为日期，columns为股票代码
        
        industry_weight为dataframe格式：
                    weight
          信息技术    0.01
          公用事业    0.02
          医疗保健    0.04
          可选消费    0.03
        
        '''
        if self.stock_choosed_type==4:
            stock_choosed=self.stock_choosed
            market_value=self.market_value
            
            result={}
            
            for date in stock_choosed:
                result[date]=weighted_in_oneday_by_value(stock_choosed[date],industry_weight,market_value.loc[date])
                
            return result  
        
        else:
            pass
    
#以下灰色注释内容为函数select_stock_industry_weighted的测试数据
#dict1=dict({'industry1':['603659.SH','603858.SH'],'industry2':['603515.SH','603568.SH']})
#dict2=dict({'industry1':['000501.SZ','603444.SH'],'industry2':['000158.SZ','000089.SZ']})
#
#stock_choosed=dict({pd.to_datetime('2019-08-30'):dict1,pd.to_datetime('2019-09-06'):dict2})
#
#ar=np.array([[0.3,0.7],[0.4,0.6]])
#indu_weight = pd.DataFrame(ar, index = [pd.to_datetime('20190830'),pd.to_datetime('20190906')], columns = ['industry1', 'industry2'])
#
#stock_df=am.stock  #stock_df应该是个股在沪深300中的权重dataframe，现在暂时没有，用am.stock这个df代替测试

#'''行业中性'''
#def select_stock_industry_weighted(stock_choosed,indu_weight,stock_df):
#    '''
#    输入说明：
#    stock_choosed为字典，格式如下：
#    {
#    ‘2019-01-01’：{‘industry1’:['000001.sz','000001.sh'.....]}  #每个行业选n个因子值最高的股票，数量可能有不同
#    ‘2019-01-02’：{‘industry2’:['000002.sh','000005.sh'.....]}
#     ....     :       ....
#     }
#    
#    indu_weight为DataFrame，格式如下：
#                 'industry1'  'industry2'
#    '2019-01-01'      0.3           0.7
#    '2019-01-02'      0.4           0.6
#    
#    stock_df为DataFrame，内容是个股的权重（在中证500/沪深300中），格式如下：
#                 '000001.sz'  '000002.sh'
#    '2019-01-01'      0.03          0.07
#    '2019-01-02'      0.05          0.6
#    
#    '''
#
#    #以下是函数主体部分
#    dict_stock_weight={}
#    '''
#    dict_stock_weight是一个字典，格式如下：
#    {
#    '2019-08-30':{'industry1':[股票1，股票2..],'industry2':[股票3，股票4..]},
#    '2019-09-06':{'industry1':[股票11，股票21..],'industry2':[股票32，股票44..]},
#    ...
#    }
#    '''
#    
#    
#    for date in list(stock_choosed.keys()):
#        #每一天
#        dict_stock_weight_everyday={}
#        '''
#        dict_stock_weight_everyday是这一天选出的所有股票及其权重信息，格式如下：
#        {'industry1':[股票1，股票2..],'industry2':[股票3，股票4..]}
#        '''
#        
#        indu_stock=stock_choosed[date]
#        #每一天的每一个行业
#        for indu in list(indu_stock.keys()):
#            #每一天每个行业里选出的股票
#            stock_indu_today=indu_stock[indu]
#            stock_weights=[]
#            #获取每一个股票当天的权重
#            for s in stock_indu_today:
#                stock_weights.append(stock_df.loc[date][s])
#            
#            
#            all_weight=sum(stock_weights)
#            
#            industry_weight=indu_weight.loc[date][indu]
#            #industry_weight是行业权重
#            
#            new_stock_weights=[x/all_weight*industry_weight for x in stock_weights]
#            #new_stockweights 储存该行业选出的股票在最终选股结果中的权重
#            
#            dict_stock_weight_1=zip(stock_indu_today,new_stock_weights)
#            '''
#            dict_stock_weight_1是这一天在这个行业内选出的股票及其权重信息，格式如下：
#            {'industry1':[股票1，股票2..]}
#            '''
#            
#            dict_stock_weight_everyday.update(dict_stock_weight_1)
#            #将这一个行业的股票权重信息添加到这一天的信息中去
#
#        dict_stock_weight[date]=dict_stock_weight_everyday
#        
#    return dict_stock_weight

                
                
                
                
    
    
